#include "device/map.h"
#include "utils.h"
#include <common.h>
#include <stdint.h> 
#include <stdio.h>
#include <elf.h>



//首先实现一个ItraceNode，记录单条指令的pc和内容，iringbuf 是节点的环形缓冲区。给外界提供两个函数来对缓冲区进行“存取”，trace_inst
//trace_inst 进行存，display_inst 用来取(展示经过反汇编的指令到屏幕上)

#ifdef CONFIG_ITRACE
#define MAX_IRINGBUG 16

typedef struct{
    word_t pc;
    word_t inst;
} ItraceNode;

ItraceNode iringbuf[MAX_IRINGBUG];


int p_cur = 0;

bool full = false;

void inst_trace(word_t pc, word_t inst){
    iringbuf[p_cur].pc = pc;
    iringbuf[p_cur].inst = inst;
    // 0-->15
    // 1-->0
    p_cur = (p_cur + 1) % MAX_IRINGBUG;//这里是循环并且加加
    full = full || p_cur ==0;//iringbuf是否满
}

void inst_display(){
    if(!full && !p_cur) return;//这里是没有满，且p_cur为0,意思是空。

    int end = p_cur;
    int i = full?p_cur:0;

    char buf[128];
    char *p;
    printf("Most recently executed instructions\n");
    void disassemble(char *str, int size, uint64_t pc, uint8_t *code, int nbyte);
    do{
        p = buf;
        p += sprintf(buf, "%s" FMT_WORD ": %08x ",(i+1)%MAX_IRINGBUG == end?" --> ":"   ",iringbuf[i].pc,iringbuf[i].inst);
        disassemble(p, buf+sizeof(buf)-p , iringbuf[i].pc, (uint8_t*)&iringbuf[i].inst, 4);
        if((i+1)%MAX_IRINGBUG==end) printf(ANSI_FG_RED);
        puts(buf);
    }while((i=(i+1)%MAX_IRINGBUG)!= end);
    puts(ANSI_NONE);
}
#endif

//；这里的内存读写踪迹，可以在访问内存时候调用，然后，就可以得到踪迹。
void pread_display(paddr_t addr, int len){
    printf("pread at " FMT_PADDR " len=%d\n", addr, len);
}

void pwrite_display(paddr_t addr, int len,word_t data){
    printf("pwrite at " FMT_PADDR " len=%d, data=" FMT_WORD "\n", addr, len, data);
}


typedef struct {
    char name[64];
    paddr_t addr;      //the function head address
    Elf32_Xword size;
} Symbol;

Symbol *symbol = NULL;  //dynamic allocate memory  or direct allocate memory (Symbol symbol[NUM])
int func_num =0;
void parse_elf(const char *elf_file)
{
    
    if(elf_file == NULL) return;
    
    FILE *fp;
    fp = fopen(elf_file, "rb");
    
    if(fp == NULL)
    {
        printf("failed to open the elf file!\n");
        //exit(0);
    }
	
    Elf32_Ehdr edhr;
	//读取elf头
    if(fread(&edhr, sizeof(Elf32_Ehdr), 1, fp) <= 0)
    {
        printf("fail to read the elf_head!\n");
        //exit(0);
    }

    if(edhr.e_ident[0] != 0x7f || edhr.e_ident[1] != 'E' || 
       edhr.e_ident[2] != 'L' ||edhr.e_ident[3] != 'F')
    {
        printf("The opened file isn't a elf file!\n");
        // exit(0);
    }
    
    fseek(fp, edhr.e_shoff, SEEK_SET);

    Elf32_Shdr shdr;
    char *string_table = NULL;
    //寻找字符串表
    for(int i = 0; i < edhr.e_shnum; i++)
    {
        if(fread(&shdr, sizeof(Elf32_Shdr), 1, fp) <= 0)
        {
            printf("fail to read the shdr\n");
            // exit(0);
        }
        
        if(shdr.sh_type == SHT_STRTAB)
        {
            //获取字符串表
            string_table = malloc(shdr.sh_size);
            fseek(fp, shdr.sh_offset, SEEK_SET);
            if(fread(string_table, shdr.sh_size, 1, fp) <= 0)
            {
                printf("fail to read the strtab\n");
                // exit(0);
            }
        }
    }
    
    //寻找符号表
    fseek(fp, edhr.e_shoff, SEEK_SET);
    
    for(int i = 0; i < edhr.e_shnum; i++)
    {
        if(fread(&shdr, sizeof(Elf32_Shdr), 1, fp) <= 0)
        {
            printf("fail to read the shdr\n");
            // exit(0);
        }

        if(shdr.sh_type == SHT_SYMTAB)
        {
            fseek(fp, shdr.sh_offset, SEEK_SET);

            Elf32_Sym sym;

            size_t sym_count = shdr.sh_size / shdr.sh_entsize;
            symbol = malloc(sizeof(Symbol) * sym_count);

            for(size_t j = 0; j < sym_count; j++)
            {
                if(fread(&sym, sizeof(Elf32_Sym), 1, fp) <= 0)
                {
                    printf("fail to read the symtab\n");
                    // exit(0);
                }

                if(ELF32_ST_TYPE(sym.st_info) == STT_FUNC)
                {
                    const char *name = string_table + sym.st_name;
                    strncpy(symbol[func_num].name, name, sizeof(symbol[func_num].name) - 1);
                    symbol[func_num].addr = sym.st_value;
                    symbol[func_num].size = sym.st_size;
                    func_num++;
                }
            }
        }
    }
    fclose(fp);
    free(string_table);
}

int rec_depth = 1;
void display_call_func(word_t pc, word_t func_addr)
{
    /*for(int i = 0; i < func_num; i++)
    {
        printf("%s\t0x%08x\t%lu\n", symbol[i].name, symbol[i].addr, symbol[i].size);
    }
    exit(0);*/
    int i = 0;
    for(; i < func_num; i++)
    {
        if(func_addr >= symbol[i].addr && func_addr < (symbol[i].addr + symbol[i].size))
        {
            break;
        }
    }
    printf("0x%08x:", pc);

    for(int k = 0; k < rec_depth; k++) printf("  ");

    rec_depth++;

    printf("call  [%s@0x%08x]\n", symbol[i].name, func_addr);
}

void display_ret_func(word_t pc)
{
    int i = 0;
    for(; i < func_num; i++)
    {
        if(pc >= symbol[i].addr && pc < (symbol[i].addr + symbol[i].size))
        {
            break;
        }
    }
    printf("0x%08x:", pc);

    rec_depth--;

    for(int k = 0; k < rec_depth; k++) printf("  ");

    printf("ret  [%s]\n", symbol[i].name);
}



//设备踪迹
void trace_dread(paddr_t addr, int len, IOMap *map) {
    printf("dtrace:read %10s at " FMT_PADDR ",%d\n",
        map->name,addr,len);
}

void trace_dwrite(paddr_t addr, int len, word_t data, IOMap *map){
    printf("dtrace: write %10s at " FMT_PADDR ", %d with " FMT_WORD "\n",
        map->name,addr,len,data);
}